package cli

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"gitee.com/dongmingchao/decent-ft/src/caretaker"
	"gitee.com/dongmingchao/decent-ft/src/courier"
	resourcePool "gitee.com/dongmingchao/decent-ft/src/resource-pool"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"syscall"
)

type Supported string

const (
	ls      Supported = "ls"
	cat     Supported = "cat"
	tree    Supported = "tree"
	show    Supported = "show"
	connect Supported = "connect"
	search  Supported = "search"
)

const prompt = ": "

func handle(callee Supported, words []string) {
	switch callee {
	case tree:
		fmt.Println(caretaker.GlobalStash)
	case ls:
		if len(caretaker.GlobalStash.Files) == 0 {
			fmt.Println("Resource pool is empty")
		}
		for _, f := range caretaker.GlobalStash.Files {
			fmt.Println(f)
		}
	case cat:
		if len(words) == 0 {
			fmt.Println("need file name or hash")
			break
		}
		var nameOrHash = words[0]
		fmt.Println("get file content", nameOrHash)
		resHashes := caretaker.GlobalStash.SearchFileHash(nameOrHash)
		if len(resHashes) == 0 {
			fmt.Println("File or hash ", nameOrHash, "not found in local")
			var target resourcePool.GNeighbor
			var hashes [][20]byte
			for _, neighbor := range caretaker.GlobalStash.Neighbors {
				for _, f := range neighbor.SearchFile(nameOrHash) {
					hashes = append(hashes, f.Checksum)
					target = neighbor
				}
			}
			if len(hashes) == 1 {
				var content *bytes.Buffer
				if target.Bypass == "" {
					// 定向邻居搜索
					content = courier.GetFileContent(target.RemoteAddr, hashes[0])
				} else {
					content = courier.BridgeGetFileContent(target.Bypass, hashes[0])
				}
				fmt.Printf("file content:\n%s", content.String())
			} else if len(hashes) == 0 {
				fmt.Println("File or hash ", nameOrHash, "not found in net neighbors")
				// 全网邻居搜索
				content := courier.BridgeSearchFileContent(nameOrHash, nil, nil)
				fmt.Printf("file content:\n%s\n", content.String())
			} else {
				fmt.Println("Too much results")
			}
		} else if len(resHashes) == 1 {
			content := caretaker.ReadStashFileByMark(resHashes[0])
			fmt.Printf("file content:\n%s", content.String())
		} else {
			fmt.Println("Too much results")
		}
	case search:
		var name = words[0]
		content := courier.BridgeSearchFileContent(name, nil, nil)
		fmt.Printf("file content:\n%s\n", content.String())
	case show:
		rAddr, err := getRemoteIndexInfo(words)
		if err == nil {
			stash := courier.GetIndexInfo(rAddr)
			fmt.Println(stash)
		}
	case connect:
		addr, err := getRemoteIndexInfo(words)
		if err == nil {
			courier.ConnectNeighbor(addr)
		}
	default:
		fmt.Println("can't understand", callee, words)
	}
}

func getRemoteIndexInfo(words []string) (*net.UDPAddr, error) {
	if len(words) == 0 {
		fmt.Println("need remote ip")
		return nil, errors.New("need remote ip")
	}
	fmt.Println("connect another resource pool", words[0])
	if words[0] != "" {
		rAddr, err := net.ResolveUDPAddr("udp", words[0])
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("connecting ", rAddr)
		return rAddr, nil
	}
	return nil, errors.New("need remote ip")
}

func Main() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	msg := make(chan string, 1)
	go func() {
		input := bufio.NewScanner(os.Stdin)
		fmt.Print(prompt)
		for {
			input.Scan()
			msg <- input.Text()
		}
	}()
loop:
	for {
		select {
		case <-sigs:
			fmt.Println("[cli] Stop")
			break loop
		case s := <-msg:
			if s != "" {
				callExpression := bufio.NewScanner(strings.NewReader(s))
				callExpression.Split(bufio.ScanWords)
				var words []string
				for callExpression.Scan() {
					words = append(words, callExpression.Text())
				}
				handle(Supported(words[0]), words[1:])
			}
			fmt.Print(prompt)
		}
	}
}
